var Q = require('q'),
    _ = require('lodash'),
    mongoose = require('mongoose'),
    departmentController = require('../../../CBO/server/controllers/Department.server.controller'),
    worklocationController = require('../../../CBO/server/controllers/WorkLocation.server.controller'),
    operationController = require('../../../CBO/server/controllers/Operation.server.controller'),
    bizRecordController = require('../../../Barcode/server/controllers/BizRecord.server.controller');

exports.queryDepartments = function (req, res) {
    departmentController.findRootDepartments().then(function (dpts) {
        var departArr = [];
        _.forEach(dpts, function (record) {
            departArr.push(record._id);
        });
        return getChildDepartment(departArr);
    }).then(function (results) {
        var obj = {};
        _.forEach(results, function (result) {
            if (!obj[result.ParentDepartment.Code]) {
                obj[result.ParentDepartment.Code] = {
                    Name: result.ParentDepartment.Name,
                    Departments: [{
                        _id: result._id, Code: result.Code, Name: result.Name
                    }]
                };
            } else {
                obj[result.ParentDepartment.Code].Departments.push({
                    _id: result._id, Code: result.Code, Name: result.Name
                });
            }
        });
        res.json({
            Data: obj,
            Error: null
        });
    }).catch(function (err) {
        res.json({
            Data: null,
            Error: err
        });
    }).done();
};

exports.queryStock = function (req, res) {
    var parameter = req.body,
        department = parameter.department,
        startTime = parameter.startTime,
        endTime = parameter.endTime;

    var stock = {}, hasSubLine = false, columns;
    departmentController.findSubDepartments(department, 'Code Name').then(function (subDpts) {
        if (subDpts.length === 0) {
            return worklocationController.findDepartmentStations(department, 'Code Name');
        } else {
            hasSubLine = true;
            columns = subDpts;
            var temp = [];
            _.forEach(subDpts, function (dpt) {
                temp.push(dpt._id);
            });
            return worklocationController.findDepartmentStations(temp, 'Code Name');
        }
    }).then(function (workLocations) {
        if (!hasSubLine) {
            columns = workLocations;
        }
        var tempWl = [];
        _.forEach(workLocations, function (workLocation) {
            tempWl.push(workLocation._id);
        });
        var durationTime = convertTime(startTime, endTime);
        return findStock(tempWl, durationTime.start, durationTime.end, hasSubLine);
    }).then(function (result) {
        if (!result) {
            res.json({
                Data: {},
                Error: null
            });
            return;
        }
        var validColumn = result.validColumn,
            validColumns = [];
        _.forEach(columns, function (column) {
            if (validColumn[column.Code]) {
                validColumns.push(column);
            }
        });
        stock.complete = result.complete;
        stock.columns = validColumns;
        res.json({
            Data: stock,
            Error: null
        });
    }).catch(function (err) {
        res.json({
            Data: null,
            Error: err
        });
    }).done();
};

function findStock (workLocations, startTime, endTime, hasSubLine) {
    var deferred = Q.defer();

    var stocks;
    findCountPoint().then(function (countPoints) {
        var args = [];
        args.push({
            $match: {
                CollectDateTime: {$gt: startTime, $lte: endTime},
                Operation: {$in: countPoints},
                WorkLocation: {$in: workLocations}
            }
        });
        args.push({
            $group: {
                _id: {BarcodeMaster: "$BarcodeMaster", WorkLocation: "$WorkLocation", Operation: '$Operation'}, 
                count: {$sum: 1}
            }
        });
        args.push({
            $project: {
                BarcodeMaster: "$_id.BarcodeMaster",
                WorkLocation: "$_id.WorkLocation",
                Operation: "$_id.Operation",
                count: 1,
                _id: 0
            }
        });
        return bizRecordController.aggregate(args);
    }).then(function (results) {
        stocks = results;
        return populateBarcodeMaster(stocks);
    }).then(function () {
        return populateWorkLocation(stocks);
    }).then(function () {
        return populateOperation(stocks);
    }).then(function () {
        if (stocks.length === 0) {
            deferred.resolve(null);
            return;
        }
        var o = {}, validColumn = {};
        if (hasSubLine) {//选择部门的下级有产线,需要返回A组B组之类
            _.forEach(stocks, function (obj) {
                var num = obj.Operation.IsTiming ? 2 : 1,
                    sequence = obj.Operation.Sequence;
                if (obj.count >= num && obj.BarcodeMaster) {
                    if (!o[obj.BarcodeMaster.ItemMasterCode]) {
                        o[obj.BarcodeMaster.ItemMasterCode] = {};
                    }
                    if (!o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Department.Code]) {
                        o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Department.Code] = {};
                    }
                    if (!o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Department.Code][sequence]) {
                        o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Department.Code][sequence] = 1;
                    } else {
                        o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Department.Code][sequence] += 1;
                    }
                    if (!validColumn[obj.WorkLocation.Department.Code]) {
                        validColumn[obj.WorkLocation.Department.Code] = true;
                    }
                }
            });
        } else {//选择部门的下级没有产线,需要返回用户
            _.forEach(stocks, function (obj) {
                var num = obj.Operation.IsTiming ? 2 : 1;
                if (obj.count >= num && obj.BarcodeMaster) {
                    if (!o[obj.BarcodeMaster.ItemMasterCode]) {
                        o[obj.BarcodeMaster.ItemMasterCode] = {};
                    }
                    if (!o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Code]) {
                        o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Code] = 1;
                    } else {
                        o[obj.BarcodeMaster.ItemMasterCode][obj.WorkLocation.Code] += 1;
                    }
                    if (!validColumn[obj.WorkLocation.Code]) {
                        validColumn[obj.WorkLocation.Code] = true;
                    }
                }
            });
        }
        deferred.resolve({
            complete: o,
            validColumn: validColumn
        });
    }).catch(function (err) {
        deferred.reject(err);
    }).done();

    return deferred.promise;
}

function getChildDepartment(dpts) {
    var deferred = Q.defer();

    departmentController.getDepartmentByParentDepartment(dpts, function (err, records) {
        if (err) {
            deferred.reject(err);
            return;
        }
        deferred.resolve(records);
    });

    return deferred.promise;
}

function findCountPoint () {
    var deferred = Q.defer();

    operationController.findCountPoint(function (err, results) {
        if (err) {
            deferred.reject(err);
            return;
        }
        deferred.resolve(results); 
    });

    return deferred.promise;
}

function populateBarcodeMaster (data) {
    var deferred = Q.defer();

    var barcodeModel = mongoose.model('BarcodeMaster');
    barcodeModel.populate(data, {path: 'BarcodeMaster', select: 'ItemMaster ItemMasterCode'}, function (err, results) {
        if (err) {
            deferred.reject(err);
            return
        }
        deferred.resolve();
    });

    return deferred.promise;
}
function populateWorkLocation(data) {
    var deferred = Q.defer();

    var departmentModel = mongoose.model('WorkLocation');
    departmentModel.populate(data, {
        path: 'WorkLocation',
        select: 'Department',
        populate: [{
            path: 'Department',
            model: 'Department',
            select: 'Code'
        }]
    }, function (err, results) {
        if (err) {
            deferred.reject(err);
            return;
        }
        deferred.resolve();
    });

    return deferred.promise;
}

function populateOperation (data) {
    var deferred = Q.defer();

    var operationModel = mongoose.model('Operation');
    operationModel.populate(data, {path: 'Operation', select: 'IsTiming Sequence'}, function (err, results) {
        if (err) {
            deferred.reject(err);
            return
        }
        deferred.resolve();
    });

    return deferred.promise;
}

function convertTime (startTime, endTime) {
    var startTimeSplit = startTime.split(':'),
        endTimeSplit = endTime.split(':'),
        startTimes = [],
        endTimes = [],
        nowDate = new Date();
    for (var i = 0; i < 3; i++) {
        if (i > startTimeSplit.length - 1) {
            startTimes[i] = 0;
        } else {
            startTimes[i] = parseInt(startTimeSplit[i]) || 0;
        }
        if (i > endTimeSplit.length - 1) {
            endTimes[i] = 0;
        } else {
            endTimes[i] = parseInt(endTimeSplit[i]) || 0;
        }
    }
    var startDateTime = new Date(nowDate.getFullYear(), nowDate.getMonth(), nowDate.getDate(), 
        startTimes[0], startTimes[1], startTimes[2]);
    var endDateTime;
    if (startTimes[0] * 3600 + startTimes[1] * 60 + startTimes[2] >= 
        endTimes[0] * 3600 + endTimes[1] * 60 + endTimes[2]
    ) {
        endDateTime = new Date(nowDate.getFullYear(), nowDate.getMonth(), nowDate.getDate() + 1, 
            endTimes[0], endTimes[1], endTimes[2]);
    } else {
        endDateTime = new Date(nowDate.getFullYear(), nowDate.getMonth(), nowDate.getDate(),
            endTimes[0], endTimes[1], endTimes[2]);
    }
    return {
        start: startDateTime,
        end: endDateTime
    };
}